using System;
using System.CodeDom.Compiler;
using System.Linq;
using Mono.Cecil;

namespace LoggingMagic.AssemlyOperations
{
    /// <summary>
    /// class for reading-writing module-level attribute which signs, that assembly was already processed
    /// </summary>
    internal class ProcessedLabel
    {
        private readonly ModuleDefinition _module;
        private readonly IAssemblyResolver _resolver;
        private const string ProcessedAttributeName = "LoggingMagicProcessed";


        public ProcessedLabel(ModuleDefinition module, IAssemblyResolver resolver)
        {
            _module = module;
            _resolver = resolver;
        }

        bool? _needsProcessing;
        public bool NeedProcessing
        {
            get
            {
                return _needsProcessing ?? (bool)(_needsProcessing = !GetAlreadyProcessed());
            }
        }

        /// <summary>
        /// Checks main module for label attribute
        /// </summary>
        /// <returns></returns>
        bool GetAlreadyProcessed()
        {
            var cga = typeof(GeneratedCodeAttribute).FullName;
            return _module.CustomAttributes.Any(i => i.AttributeType.FullName == cga && i.HasConstructorArguments && Convert.ToString(i.ConstructorArguments[0].Value) == ProcessedAttributeName);
        }

        /// <summary>
        /// Marks main module with label attribute
        /// </summary>
        public void MarkAsProcessed()
        {
            _module.CustomAttributes.Add(CreateLabel());
        }

        /// <summary>
        /// Creates a label class used to mark module as processed
        /// </summary>
        private CustomAttribute CreateLabel()
        {
            var stringType = _module.TypeSystem.String;
            var corlib = (AssemblyNameReference)_module.TypeSystem.Corlib;
            var system = _resolver.Resolve(new AssemblyNameReference("System", corlib.Version)
            {
                PublicKeyToken = corlib.PublicKeyToken,
            });

            var generatedCodeAttribute = system.MainModule.GetType("System.CodeDom.Compiler.GeneratedCodeAttribute");
            var generatedCodeCtor = generatedCodeAttribute.Methods.First(m => m.IsConstructor && m.Parameters.Count == 2);

            var result = new CustomAttribute(_module.Import(generatedCodeCtor));
            result.ConstructorArguments.Add(new CustomAttributeArgument(stringType, ProcessedAttributeName));
            result.ConstructorArguments.Add(new CustomAttributeArgument(stringType, "1.0"));

            return result;
        }
    }
}